using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Web;
using PickGold.Charting.ChartTypes;
using PickGold.Charting.Design;

namespace PickGold.Charting
{
	[DefaultProperty("Enabled"), SRDescription("DescriptionAttributeAxisScaleBreakStyle_AxisScaleBreakStyle"), AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal), AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
	public class AxisScaleBreakStyle
	{
		private Color _breakLineColor;
		private ChartDashStyle _breakLineDashStyle;
		private BreakLineStyle _breakLineStyle;
		private int _breakLineWidth;
		private bool _enabled;
		private int _maximumNumberOfBreaks;
		private int _minimumNumberOfEmptySegments;
		private double _minSegmentSize;
		private double _segmentSpacing;
		private StartFromZero _startFromZero;
		private int _totalNumberOfSegments;
		internal Axis axis;

		public AxisScaleBreakStyle()
		{
			this._breakLineStyle = BreakLineStyle.Ragged;
			this._segmentSpacing = 1.5;
			this._breakLineColor = Color.Black;
			this._breakLineWidth = 1;
			this._breakLineDashStyle = ChartDashStyle.Solid;
			this._minSegmentSize = 10.0;
			this._totalNumberOfSegments = 100;
			this._minimumNumberOfEmptySegments = 0x19;
			this._maximumNumberOfBreaks = 2;
		}

		internal AxisScaleBreakStyle(Axis axis)
		{
			this._breakLineStyle = BreakLineStyle.Ragged;
			this._segmentSpacing = 1.5;
			this._breakLineColor = Color.Black;
			this._breakLineWidth = 1;
			this._breakLineDashStyle = ChartDashStyle.Solid;
			this._minSegmentSize = 10.0;
			this._totalNumberOfSegments = 100;
			this._minimumNumberOfEmptySegments = 0x19;
			this._maximumNumberOfBreaks = 2;
			this.axis = axis;
		}

		internal bool CanUseAxisScaleBreaks()
		{
			if (((this.axis == null) || (this.axis.ChartArea == null)) || (this.axis.ChartArea.Common.Chart == null))
			{
				return false;
			}
			if (this.axis.ChartArea.Area3DStyle.Enable3D)
			{
				return false;
			}
			if ((this.axis.axisType == AxisName.X) || (this.axis.axisType == AxisName.X2))
			{
				return false;
			}
			if (this.axis.IsLogarithmic)
			{
				return false;
			}
			if (this.axis.ScaleView.IsZoomed)
			{
				return false;
			}
			foreach (Series series in GetAxisSeries(this.axis))
			{
				if ((series.ChartType == SeriesChartType.Renko) || (series.ChartType == SeriesChartType.PointAndFigure))
				{
					return false;
				}
				IChartType chartType = this.axis.ChartArea.Common.ChartTypeRegistry.GetChartType(series.ChartTypeName);
				if (chartType == null)
				{
					return false;
				}
				if ((chartType.CircularChartArea || chartType.Stacked) || !chartType.RequireAxes)
				{
					return false;
				}
			}
			return true;
		}

		private void FillAxisSegmentCollection(AxisScaleSegmentCollection axisSegments)
		{
			axisSegments.Clear();
			double minYValue = 0.0;
			double maxYValue = 0.0;
			double segmentSize = 0.0;
			double[] segmentMaxValue = null;
			double[] segmentMinValue = null;
			int[] segmentPointNumber = this.GetSeriesDataStatistics(this._totalNumberOfSegments, out minYValue, out maxYValue, out segmentSize, out segmentMaxValue, out segmentMinValue);
			if (segmentPointNumber != null)
			{
				double minimumValue = minYValue;
				double maximumValue = maxYValue;
				this.axis.EstimateNumberAxis(ref minimumValue, ref maximumValue, this.axis.IsStartedFromZero, this.axis.prefferedNumberofIntervals, true, true);
				if (maxYValue != minYValue)
				{
					double num6 = (maxYValue - minYValue) / ((maximumValue - minimumValue) / 100.0);
					ArrayList excludedSegments = new ArrayList();
					bool flag = false;
					while (!flag)
					{
						flag = true;
						int startSegment = 0;
						int numberOfSegments = 0;
						this.GetLargestSequenseOfSegmentsWithNoPoints(segmentPointNumber, out startSegment, out numberOfSegments);
						int num9 = (int)(this._minimumNumberOfEmptySegments * (100.0 / num6));
						if ((axisSegments.Count > 0) && (numberOfSegments > 0))
						{
							foreach (AxisScaleSegment segment in axisSegments)
							{
								if (((startSegment > 0) && ((startSegment + numberOfSegments) <= (segmentMaxValue.Length - 1))) && ((segmentMaxValue[startSegment - 1] >= segment.ScaleMinimum) && (segmentMinValue[startSegment + numberOfSegments] <= segment.ScaleMaximum)))
								{
									double num10 = segment.ScaleMaximum - segment.ScaleMinimum;
									double num11 = segmentMinValue[startSegment + numberOfSegments] - segmentMaxValue[startSegment - 1];
									double num12 = num11 / (num10 / 100.0);
									num12 = (num12 / 100.0) * segment.Size;
									if ((num12 > num9) && (numberOfSegments > this._minSegmentSize))
									{
										num9 = numberOfSegments;
									}
								}
							}
						}
						if (numberOfSegments >= num9)
						{
							flag = false;
							excludedSegments.Add(startSegment);
							excludedSegments.Add(numberOfSegments);
							axisSegments.Clear();
							if (excludedSegments.Count > 0)
							{
								double naN = double.NaN;
								double num14 = double.NaN;
								int num15 = 0;
								for (int i = 0; i < segmentPointNumber.Length; i++)
								{
									bool flag2 = this.IsExcludedSegment(excludedSegments, i);
									if ((!flag2 && !double.IsNaN(segmentMinValue[i])) && !double.IsNaN(segmentMaxValue[i]))
									{
										num15 += segmentPointNumber[i];
										if (double.IsNaN(naN))
										{
											naN = segmentMinValue[i];
											num14 = segmentMaxValue[i];
										}
										else
										{
											num14 = segmentMaxValue[i];
										}
									}
									if (!double.IsNaN(naN) && (flag2 || (i == (segmentPointNumber.Length - 1))))
									{
										if (num14 == naN)
										{
											naN -= segmentSize;
											num14 += segmentSize;
										}
										AxisScaleSegment segment2 = new AxisScaleSegment();
										segment2.ScaleMaximum = num14;
										segment2.ScaleMinimum = naN;
										segment2.Tag = num15;
										axisSegments.Add(segment2);
										naN = double.NaN;
										num14 = double.NaN;
										num15 = 0;
									}
								}
							}
							this.SetAxisSegmentPosition(axisSegments);
						}
						if ((axisSegments.Count - 1) >= this._maximumNumberOfBreaks)
						{
							flag = true;
						}
					}
				}
			}
		}

		internal void GetAxisSegmentForScaleBreaks(AxisScaleSegmentCollection axisSegments)
		{
			axisSegments.Clear();
			if (this.IsEnabled())
			{
				this.FillAxisSegmentCollection(axisSegments);
				if (axisSegments.Count >= 1)
				{
					int startScaleFromZeroSegmentIndex = this.GetStartScaleFromZeroSegmentIndex(axisSegments);
					int num2 = 0;
					foreach (AxisScaleSegment segment in axisSegments)
					{
						bool shouldStartFromZero = num2 == startScaleFromZeroSegmentIndex;
						double scaleMinimum = segment.ScaleMinimum;
						double scaleMaximum = segment.ScaleMaximum;
						segment.Interval = this.axis.EstimateNumberAxis(ref scaleMinimum, ref scaleMaximum, shouldStartFromZero, this.axis.prefferedNumberofIntervals, true, true);
						segment.ScaleMinimum = scaleMinimum;
						segment.ScaleMaximum = scaleMaximum;
						if (segment.ScaleMinimum < this.axis.Minimum)
						{
							segment.ScaleMinimum = this.axis.Minimum;
						}
						if (segment.ScaleMaximum > this.axis.Maximum)
						{
							segment.ScaleMaximum = this.axis.Maximum;
						}
						num2++;
					}
					bool flag2 = false;
					AxisScaleSegment segment2 = axisSegments[0];
					for (int i = 1; i < axisSegments.Count; i++)
					{
						AxisScaleSegment segment3 = axisSegments[i];
						if (segment3.ScaleMinimum <= segment2.ScaleMaximum)
						{
							if (segment3.ScaleMaximum > segment2.ScaleMaximum)
							{
								segment2.ScaleMaximum = segment3.ScaleMaximum;
							}
							flag2 = true;
							axisSegments.RemoveAt(i);
							i--;
						}
						else
						{
							segment2 = segment3;
						}
					}
					if (flag2)
					{
						this.SetAxisSegmentPosition(axisSegments);
					}
				}
			}
		}

		internal static List<Series> GetAxisSeries(Axis axis)
		{
			var list = new List<Series>();
			if (((axis != null) && (axis.ChartArea != null)) && (axis.ChartArea.Common.Chart != null))
			{
				foreach (var series in axis.ChartArea.Common.Chart.Series)
				{
					if ((((series.ChartArea == axis.ChartArea.Name) && series.Enabled) && ((axis.axisType != AxisName.Y) || (series.YAxisType != AxisType.Secondary))) && ((axis.axisType != AxisName.Y2) || (series.YAxisType != AxisType.Primary)))
						list.Add(series);
				}
			}
			return list;
		}

		internal bool GetLargestSequenseOfSegmentsWithNoPoints(int[] segmentPointNumber, out int startSegment, out int numberOfSegments)
		{
			startSegment = -1;
			numberOfSegments = 0;
			int num = -1;
			int num2 = -1;
			for (int i = 0; i < segmentPointNumber.Length; i++)
			{
				if (segmentPointNumber[i] == 0)
				{
					if (num == -1)
					{
						num = i;
						num2 = 1;
					}
					else
					{
						num2++;
					}
				}
				if ((num2 > 0) && ((segmentPointNumber[i] != 0) || (i == (segmentPointNumber.Length - 1))))
				{
					if (num2 > numberOfSegments)
					{
						startSegment = num;
						numberOfSegments = num2;
					}
					num = -1;
					num2 = 0;
				}
			}
			if (numberOfSegments == 0)
			{
				return false;
			}
			for (int j = startSegment; j < (startSegment + numberOfSegments); j++)
			{
				segmentPointNumber[j] = -1;
			}
			return true;
		}

		internal int[] GetSeriesDataStatistics(int segmentCount, out double minYValue, out double maxYValue, out double segmentSize, out double[] segmentMaxValue, out double[] segmentMinValue)
		{
			var axisSeries = GetAxisSeries(this.axis);
			minYValue = 0.0;
			maxYValue = 0.0;
			this.axis.Common.DataManager.GetMinMaxYValue(axisSeries, out minYValue, out maxYValue);
			if (axisSeries.Count == 0)
			{
				segmentSize = 0.0;
				segmentMaxValue = null;
				segmentMinValue = null;
				return null;
			}
			segmentSize = (maxYValue - minYValue) / ((double)segmentCount);
			int[] numArray = new int[segmentCount];
			segmentMaxValue = new double[segmentCount];
			segmentMinValue = new double[segmentCount];
			for (int i = 0; i < segmentCount; i++)
			{
				segmentMaxValue[i] = double.NaN;
				segmentMinValue[i] = double.NaN;
			}
			foreach (var series in axisSeries)
			{
				var yValuesPerPoint = 1;
				var chartType = this.axis.ChartArea.Common.ChartTypeRegistry.GetChartType(series.ChartTypeName);
				if (((chartType != null) && chartType.ExtraYValuesConnectedToYAxis) && (chartType.YValuesPerPoint > 1))
				{
					yValuesPerPoint = chartType.YValuesPerPoint;
				}
				foreach (var point in series.Points)
				{
					if (!point.IsEmpty)
					{
						for (int j = 0; j < yValuesPerPoint; j++)
						{
							int index = (int)Math.Floor((double)((point.YValues[j] - minYValue) / segmentSize));
							if (index < 0)
							{
								index = 0;
							}
							if (index > (segmentCount - 1))
							{
								index = segmentCount - 1;
							}
							numArray[index]++;
							if (numArray[index] == 1)
							{
								segmentMaxValue[index] = point.YValues[j];
								segmentMinValue[index] = point.YValues[j];
							}
							else
							{
								segmentMaxValue[index] = Math.Max(segmentMaxValue[index], point.YValues[j]);
								segmentMinValue[index] = Math.Min(segmentMinValue[index], point.YValues[j]);
							}
						}
					}
				}
			}
			return numArray;
		}

		private int GetStartScaleFromZeroSegmentIndex(AxisScaleSegmentCollection axisSegments)
		{
			if ((this.StartFromZero == StartFromZero.Auto) || (this.StartFromZero == StartFromZero.Yes))
			{
				int num = 0;
				foreach (AxisScaleSegment segment in axisSegments)
				{
					if ((segment.ScaleMinimum < 0.0) && (segment.ScaleMaximum > 0.0))
					{
						return -1;
					}
					if ((segment.ScaleMinimum > 0.0) || (num == (axisSegments.Count - 1)))
					{
						if ((this.StartFromZero == StartFromZero.Auto) && (segment.ScaleMinimum > (2.0 * (segment.ScaleMaximum - segment.ScaleMinimum))))
						{
							return -1;
						}
						return num;
					}
					num++;
				}
			}
			return -1;
		}

		private void Invalidate()
		{
			if (this.axis != null)
			{
				this.axis.Invalidate();
			}
		}

		internal bool IsEnabled()
		{
			return (this.Enabled && this.CanUseAxisScaleBreaks());
		}

		private bool IsExcludedSegment(ArrayList excludedSegments, int segmentIndex)
		{
			for (int i = 0; i < excludedSegments.Count; i += 2)
			{
				if ((segmentIndex >= ((int)excludedSegments[i])) && (segmentIndex < (((int)excludedSegments[i]) + ((int)excludedSegments[i + 1]))))
				{
					return true;
				}
			}
			return false;
		}

		private void SetAxisSegmentPosition(AxisScaleSegmentCollection axisSegments)
		{
			int num = 0;
			foreach (AxisScaleSegment segment in axisSegments)
			{
				if (segment.Tag is int)
				{
					num += (int)segment.Tag;
				}
			}
			double num2 = Math.Min(this._minSegmentSize, Math.Floor((double)(100.0 / ((double)axisSegments.Count))));
			double num3 = 0.0;
			for (int i = 0; i < axisSegments.Count; i++)
			{
				axisSegments[i].Position = (num3 > 100.0) ? 100.0 : num3;
				axisSegments[i].Size = Math.Round((double)(((double)((int)axisSegments[i].Tag)) / (((double)num) / 100.0)), 5);
				if (axisSegments[i].Size < num2)
				{
					axisSegments[i].Size = num2;
				}
				if (i < (axisSegments.Count - 1))
				{
					axisSegments[i].Spacing = this._segmentSpacing;
				}
				num3 += axisSegments[i].Size;
			}
			double num5 = 0.0;
			do
			{
				num5 = 0.0;
				double minValue = double.MinValue;
				int num7 = -1;
				for (int j = 0; j < axisSegments.Count; j++)
				{
					num5 += axisSegments[j].Size;
					if (axisSegments[j].Size > minValue)
					{
						minValue = axisSegments[j].Size;
						num7 = j;
					}
				}
				if (num5 > 100.0)
				{
					AxisScaleSegment segment1 = axisSegments[num7];
					segment1.Size -= num5 - 100.0;
					if (axisSegments[num7].Size < num2)
					{
						axisSegments[num7].Size = num2;
					}
					double num9 = axisSegments[num7].Position + axisSegments[num7].Size;
					for (int k = num7 + 1; k < axisSegments.Count; k++)
					{
						axisSegments[k].Position = num9;
						num9 += axisSegments[k].Size;
					}
				}
			}
			while (num5 > 100.0);
		}

		[DefaultValue(3), SRDescription("DescriptionAttributeAxisScaleBreakStyle_BreakLineType"), SRCategory("CategoryAttributeAppearance")]
		public BreakLineStyle BreakLineStyle
		{
			get
			{
				return this._breakLineStyle;
			}
			set
			{
				this._breakLineStyle = value;
				this.Invalidate();
			}
		}

		[SRCategory("CategoryAttributeMisc"), SRDescription("DescriptionAttributeAxisScaleBreakStyle_CollapsibleSpaceThreshold"), DefaultValue(0x19)]
		public int CollapsibleSpaceThreshold
		{
			get
			{
				return this._minimumNumberOfEmptySegments;
			}
			set
			{
				if ((value < 10) || (value > 90))
				{
					throw new ArgumentOutOfRangeException("value", SR.ExceptionAxisScaleBreaksCollapsibleSpaceInvalid);
				}
				this._minimumNumberOfEmptySegments = value;
				this.Invalidate();
			}
		}

		[SRCategory("CategoryAttributeMisc"), DefaultValue(false), SRDescription("DescriptionAttributeAxisScaleBreakStyle_Enabled"), ParenthesizePropertyName(true)]
		public bool Enabled
		{
			get
			{
				return this._enabled;
			}
			set
			{
				this._enabled = value;
				this.Invalidate();
			}
		}

		[TypeConverter(typeof(ColorConverter)), SRDescription("DescriptionAttributeLineColor"), SRCategory("CategoryAttributeAppearance"), Editor(typeof(ChartColorEditor), typeof(UITypeEditor)), DefaultValue(typeof(Color), "Black")]
		public Color LineColor
		{
			get
			{
				return this._breakLineColor;
			}
			set
			{
				this._breakLineColor = value;
				this.Invalidate();
			}
		}

		[SRCategory("CategoryAttributeAppearance"), DefaultValue(5), SRDescription("DescriptionAttributeLineDashStyle")]
		public ChartDashStyle LineDashStyle
		{
			get
			{
				return this._breakLineDashStyle;
			}
			set
			{
				this._breakLineDashStyle = value;
				this.Invalidate();
			}
		}

		[DefaultValue(1), SRCategory("CategoryAttributeAppearance"), SRDescription("DescriptionAttributeLineWidth")]
		public int LineWidth
		{
			get
			{
				return this._breakLineWidth;
			}
			set
			{
				if ((value < 1.0) || (value > 10))
				{
					throw new ArgumentOutOfRangeException("value", SR.ExceptionAxisScaleBreaksLineWidthInvalid);
				}
				this._breakLineWidth = value;
				this.Invalidate();
			}
		}

		[SRDescription("DescriptionAttributeAxisScaleBreakStyle_MaxNumberOfBreaks"), SRCategory("CategoryAttributeMisc"), DefaultValue(2)]
		public int MaxNumberOfBreaks
		{
			get
			{
				return this._maximumNumberOfBreaks;
			}
			set
			{
				if ((value < 1) || (value > 5))
				{
					throw new ArgumentOutOfRangeException("value", SR.ExceptionAxisScaleBreaksNumberInvalid);
				}
				this._maximumNumberOfBreaks = value;
				this.Invalidate();
			}
		}

		[SRDescription("DescriptionAttributeAxisScaleBreakStyle_Spacing"), SRCategory("CategoryAttributeMisc"), DefaultValue((double)1.5)]
		public double Spacing
		{
			get
			{
				return this._segmentSpacing;
			}
			set
			{
				if ((value < 0.0) || (value > 10.0))
				{
					throw new ArgumentOutOfRangeException("value", SR.ExceptionAxisScaleBreaksSpacingInvalid);
				}
				this._segmentSpacing = value;
				this.Invalidate();
			}
		}

		[SRCategory("CategoryAttributeMisc"), SRDescription("DescriptionAttributeAxisScaleBreakStyle_StartFromZero"), DefaultValue(0)]
		public StartFromZero StartFromZero
		{
			get
			{
				return this._startFromZero;
			}
			set
			{
				this._startFromZero = value;
				this.Invalidate();
			}
		}
	}
}

